<?php

namespace Biz\Services\Site;

use Biz\Criteria\Site\Menu\PositionFilterCriterion;
use Biz\Criteria\Site\Menu\RootFilterCriterion;
use Lumen\Logger\Logger;
use Illuminate\Http\Request;
use Biz\Models\Site\SiteMenu;
use Biz\Libraries\TranslationUtil;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Biz\Constants\Site\SiteMenuConstants;
use Biz\Criteria\Site\SiteFilterCriterion;
use Biz\Criteria\Site\LocaleFilterCriterion;
use Lumen\Core\Exceptions\BusinessException;
use Biz\Repositories\Site\SiteMenuRepository;
use Illuminate\Contracts\Pagination\Paginator;

/**
 * SiteMenuService.
 *
 * @license [http://www.85do.com] [杭州永奥网络科技有限公司]
 * @copyright Copyright (c) 2018-2026 Hangzhou Yongao Technology Co., Ltd. All rights reserved.
 */
class SiteMenuService
{
    /**
     * @var \Lumen\Repository\Contracts\RepositoryContract
     */
    protected $repository;

    /**
     * @var SiteMenuModuleService
     */
    protected $menuModuleService;

    /**
     * RepositoryController constructor.
     *
     * @param SiteMenuRepository    $repository
     * @param SiteMenuModuleService $menuModuleService
     */
    public function __construct(SiteMenuRepository $repository, SiteMenuModuleService $menuModuleService)
    {
        $this->repository                     = $repository;
        $this->menuModuleService              = $menuModuleService;
    }

    /**
     * Get.
     *
     * @param Request $request
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|mixed
     */
    public function getAll(Request $request)
    {
        $site_id = $request->get('site_id');

        $results = $this->repository
            ->with(['parent', 'children', 'translations'])
            ->pushCriterion(new SiteFilterCriterion($site_id))
            ->findAll();

        return $results;
    }

    /**
     * Get.
     *
     * @param Request $request
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|mixed
     */
    public function getAllRoot(Request $request)
    {
        $site_id = $request->get('site_id');

        $results = $this->repository
            ->with(['parent', 'children', 'translations'])
            ->pushCriterion(new SiteFilterCriterion($site_id))
            ->pushCriterion(new RootFilterCriterion())
            ->findAll();

        return $results;
    }

    /**
     * Get.
     *
     * @param Request $request
     * @param string $position
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|mixed
     */
    public function getAllByPosition(Request $request, string $position)
    {
        $site_id = $request->get('site_id');

        $results = $this->repository
            ->with(['parent', 'children', 'translations'])
            ->pushCriterion(new SiteFilterCriterion($site_id))
            ->pushCriterion(new RootFilterCriterion())
            ->pushCriterion(new PositionFilterCriterion($position))
            ->findAll();

        return $results;
    }

    /**
     * Get.
     *
     * @param Request $request
     * @param string  $url
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|mixed
     */
    public function url(Request $request, string $url)
    {
        $site_id = $request->get('site_id');

        $menu = $this->repository
            ->with(['parent', 'children', 'translations'])
            ->pushCriterion(new SiteFilterCriterion($site_id))
            ->where('url', $url)
            ->findFirst();

        return $menu;
    }

    /**
     * 列表页.
     *
     * @param Request $request
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|mixed
     */
    public function index(Request $request): Paginator
    {
        $site_id  = $request->get('site_id');
        $pageSize = $request->get('page_size', 10);

        $results = $this->repository
            ->with(['parent', 'children', 'translations'])
            ->setCacheLifetime(0)
            ->pushCriterion(new SiteFilterCriterion($site_id))
            //->pushCriterion(new LocaleFilterCriterion())
            ->paginate($pageSize);

        return $results;
    }

    /**
     * Find.
     *
     * @param int $id
     *
     * @throws BusinessException
     *
     * @return SiteMenu
     */
    public function find(int $id): SiteMenu
    {
        $menu = $this->repository->find($id);
        if (is_null($menu)) {
            throw new BusinessException(trans('site/menu.not_found'));
        }

        return $menu;
    }

    /**
     * Find By uid.
     *
     * @param string $uid
     *
     * @throws BusinessException
     *
     * @return SiteMenu
     */
    public function findByUid(string $uid): SiteMenu
    {
        $id   = SiteMenu::decodeUid($uid);

        return $this->find($id);
    }

    /**
     * With find.
     *
     * @param int   $id
     * @param array $with
     *
     * @throws BusinessException
     *
     * @return SiteMenu
     */
    public function findWith(int $id, $with = []): SiteMenu
    {
        $site = $this->repository->with($with)->find($id);
        if (is_null($site)) {
            throw new BusinessException(trans('site/menu.not_found'));
        }

        return $site;
    }

    /**
     * Find By Site Id.
     *
     * @param int $site_id
     *
     * @throws BusinessException
     *
     * @return bool
     */
    public function findBySiteId(int $site_id)
    {
        $menu = $this->repository
            ->pushCriterion(new SiteFilterCriterion($site_id))
            ->findFirst();

        return $menu;
    }

    /**
     * Find By Site Id.
     *
     * @param int    $site_id
     * @param string $module
     * @param string $url
     *
     * @throws BusinessException
     *
     * @return bool
     */
    public function findByRepeatURL(int $site_id, string $module, string $url)
    {
        $menu = $this->repository
            ->pushCriterion(new SiteFilterCriterion($site_id))
            ->where('module', $module)
            ->where('url', $url)
            ->findFirst();

        return $menu;
    }

    /**
     * Handle Url.
     *
     * @param array $data
     *
     * @throws BusinessException
     *
     * @return void
     */
    public function checkRepeatURL(array $data): void
    {
        $url     = $data['url'];
        $site_id = $data['site_id'];
        if (SiteMenuConstants::URL_NONE === $url) {
            $data['url'] = SiteMenuConstants::URL_DEFAULT;
        } else {
            $module    = $data['module'];
            /** @var SiteMenu $parent */
            $menu = $this->findByRepeatURL($site_id, $module, $url);
            if ($menu) {
                throw new BusinessException(trans('site/menu.module_url_repeat'));
            }
        }
    }

    /**
     * Handle Parent Id.
     *
     * @param array $data
     *
     * @return array
     */
    public function handleParentId(array $data): array
    {
        $parent_id = $data['parent_id'];
        if (SiteMenuConstants::PARENT_ROOT === $parent_id) {
            $data['parent_id'] = SiteMenuConstants::PARENT_DEFAULT;
        } else {
            /** @var SiteMenu $parent */
            $parent            = $this->findByUid($parent_id);
            $data['parent_id'] = $parent->id;
        }

        return $data;
    }

    /**
     * Handle Module Target Id.
     *
     * @param array $data
     *
     * @return array
     */
    public function handleModuleTargetId(array $data): array
    {
        $module    = $data['module'];
        $target_id = $data['target_id'];
        if (SiteMenuConstants::TARGET_LIST === $target_id) {
            $data['target_id'] = SiteMenuConstants::TARGET_DEFAULT;
        } else {
            /** @var Model $targetModule */
            $targetModule       = $this->menuModuleService->getTarget($module, $target_id);
            $data['target_id']  = $targetModule->getKey();
        }

        return $data;
    }

    /**
     * Create.
     *
     * @param array $data
     *
     * @throws \Exception
     *
     * @return SiteMenu
     */
    public function create(array $data): SiteMenu
    {
        // Handle Url
        $this->checkRepeatURL($data);

        // Handle Parent
        $data = $this->handleParentId($data);

        // Handle Module
        $data = $this->handleModuleTargetId($data);

        // 1) Start Transaction
        DB::beginTransaction();
        try {
            // 创建介绍
            $data['status'] = SiteMenuConstants::STATUS_NORMAL;

            $data = TranslationUtil::convertTranslationsData($data);

            $menu   = $this->repository->create($data);
            if (! $menu) {
                throw new BusinessException(trans('site/menu.create_fail'));
            }
            // Load Translations
            $menu->load('translations');

            // 2) Commit Transaction
            DB::commit();
        } catch (\Exception $e) {
            // 3) Rollback Transaction
            DB::rollback();
            // 4) Log Transaction
            Logger::error(__CLASS__, __FUNCTION__, $e);
            // 5) Throw Exception
            throw $e;
        }

        return $menu;
    }

    /**
     * Detail.
     *
     * @param string $id
     *
     * @throws BusinessException
     *
     * @return SiteMenu
     */
    public function show(string $id): SiteMenu
    {
        $id   = SiteMenu::decodeUid($id);
        $site = $this->findWith($id, ['parent', 'children', 'translations']);

        return $site;
    }

    /**
     * Edit.
     *
     * @param string $id
     * @param array  $data
     *
     * @throws \Exception
     *
     * @return SiteMenu
     */
    public function edit(string $id, array $data): SiteMenu
    {
        $id   = SiteMenu::decodeUid($id);

        return $this->update($id, $data);
    }

    /**
     * Update.
     *
     * @param int   $id
     * @param array $data
     *
     * @throws \Exception
     *
     * @return SiteMenu
     */
    public function update(int $id, array $data): SiteMenu
    {
        $menu = $this->find($id);

        // Handle Url
        if ($menu->url !== $data['url']) {
            $this->checkRepeatURL($data);
        }

        // Handle Parent
        $data = $this->handleParentId($data);

        // Handle Module
        $data = $this->handleModuleTargetId($data);

        // 1) Start Transaction
        DB::beginTransaction();
        try {
            $data = TranslationUtil::convertTranslationsData($data);

            $menu   = $this->repository->update($menu, $data);
            if (! $menu) {
                throw new BusinessException(trans('site/menu.create_fail'));
            }

            // 2) Commit Transaction
            DB::commit();
        } catch (\Exception $e) {
            // 3) Rollback Transaction
            DB::rollback();
            // 4) Log Transaction
            Logger::error(__CLASS__, __FUNCTION__, $e);
            // 5) Throw Exception
            throw $e;
        }

        return $menu;
    }

    /**
     * Remove.
     *
     * @param string $id
     *
     * @throws \Exception
     *
     * @return bool
     */
    public function remove(string $id): bool
    {
        $id   = SiteMenu::decodeUid($id);

        return $this->delete($id);
    }

    /**
     * Delete.
     *
     * @param int $id
     *
     * @throws \Exception
     *
     * @return bool
     */
    public function delete(int $id): bool
    {
        /** @var SiteMenu $menu */
        $menu = $this->find($id);

        // 1) Start Transaction
        DB::beginTransaction();
        try {
            $this->repository->delete($menu);

            // 2) Commit Transaction
            DB::commit();
        } catch (\Exception $e) {
            // 3) Rollback Transaction
            DB::rollback();
            // 4) Log Transaction
            Logger::error(__CLASS__, __FUNCTION__, $e);
            // 5) Throw Exception
            throw $e;
        }

        return true;
    }
}
